/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package motiveai.components;

import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import motiveai.factors.Environment;
import motiveai.factors.EnvironmentDefinition;

/**
 *
 * @author oxplay
 */
public abstract class Agent {

    private Environment actualEnvironment;
    private MotiveAI manager;
    private float awakeTime = 0;
    private boolean needRemove = false;
    private Map<String, Float> behaviourExhaustionList = new HashMap<String, Float>();

    public void removeGroup(String name) {
        AgentGroup group = manager.getGroup(name);
        if(group != null){
            group.agentList.remove(this);
            if(group.agentList.size() <= 0){
                manager.removeGroup(group);
            }
        }
    }

    public void removeGroup(AgentGroup group) {
        manager.getGroup(group.getName()).agentList.remove(this);
        if(group.agentList.size() <= 0){
            manager.removeGroup(group);
        }
    }

    public void addGroup(AgentGroup group) {
        AgentGroup managergroup = manager.getGroup(group.getName());
        if(managergroup != null){
            managergroup.agentList.add(this);
        } else {
            group.agentList.add(this);
            manager.addGroup(group);
        }
    }

    /*
     * Return true, if behaviour can be used
     * Return false if behaviour can't be used - need to wait.
     */
    public boolean canUseBehaviour(String name) {
        if (behaviourExhaustionList.containsKey(name)) {
            return false;
        }
        return true;
    }

    /*
     * Set that type of behaviour unusable(for that agent) for period of time defined in that behaviour
     */
    public void addBehaviourExhaustion(BehaviourDescription description) {
        behaviourExhaustionList.put(description.behaviourName, description.behaviourTime);
    }

    /*
     * Execute agent manager removeAgent(T agent) method
     */
    public void remove() {
        onRemove();
        this.needRemove = true;
    }

    protected boolean needToRemove() {
        return needRemove;
    }

    public void setAwakeTime(float awakeTime) {
        this.awakeTime = awakeTime;
    }

    public float getAwakeTime() {
        return awakeTime;
    }

    protected boolean update(float tpf) {
        if (behaviourExhaustionList.size() > 0) {
            for (Iterator<String> it = behaviourExhaustionList.keySet().iterator(); it.hasNext();) {
                String behaviourName = it.next();
                float left = behaviourExhaustionList.get(behaviourName);
                float updatedValue = left - tpf;
                if (updatedValue > 0) {
                    behaviourExhaustionList.put(behaviourName, left - tpf);
                } else {
                    it.remove();
                }
            }
        }
        if (awakeTime > 0) {
            awakeTime -= tpf;
        }
        if (awakeTime <= 0) {
            onUpdate();
            return true;
        } else {
            return false;
        }
    }

    /*
     * Set Manager for that agent
     */
    protected void setManager(MotiveAI manager) {
        this.manager = manager;
    }

    /*
     * Return Manager that agent is assigned, null if Manager is not set.
     */
    public MotiveAI getManager() {
        return this.manager;
    }

    /*
     * execute Behaviour on current Agent
     */
    public void behave(BehaviourEntity behaviour, int maxCountLearn) {
        behaviour.execute(this, maxCountLearn);
    }

    /*
     * Set actual agent Environment, only for AI system. don't use it out of it.
     */
    protected void setAgentEnvironment(Environment actualEnvironment) {
        this.actualEnvironment = actualEnvironment;
    }

    /*
     * Initialize actual agent Environment
     */
    public void initAgentEnvironment(EnvironmentDefinition environmentDefinition) {
        this.actualEnvironment = environmentDefinition.generateDefaultEnvironment();
    }

    /*
     * Return actual agent Environment
     */
    protected Environment getAgentEnvironment() {
        return this.actualEnvironment;
    }

    public abstract void onUpdate();

    public abstract void onRemove();

    public abstract void onAdd();
}
